package scparser.factory.primitiveFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Point3i;
import javax.vecmath.Vector3d;

import lib.common.Constants;
import model.Scene;
import model.primitive.GenericMesh;
import model.primitive.Primitive;
import model.primitive.TriangleOld;
import scparser.ParserHelper;

public class GenericMeshFactory extends PrimitiveFactory {
	private List<Point3d> points;
	private List<TriangleOld> triangles;
	private List<Point3i> triangleIdexes;
	private Map<Point3d, Point3d> normals;
	private Map<Point3d, Point2d> uvs;

	
	public GenericMeshFactory(Scene scene) {
		super.scene = scene;
	}
	
	public Primitive create(List<String> block) throws FactoryException {
		String line = null;
		Iterator<String> it = block.iterator();
		
		try {
			while (it.hasNext()) {
				line = it.next().trim();
				if (line.equals(Constants.S_CLOSE)) {
					break;
				} else if (line.startsWith("name")) {
					this.name = ParserHelper.takeString(line, "name");
				} else if (line.startsWith("shader")) {
					String shaderName = ParserHelper.takeString(line, "shader");
					if ((this.shader = getShader(shaderName)) == null) {
						throw new FactoryException("shader " + shaderName + " not found");
					}			
				} else if (line.startsWith("triangles")) {
					int num = Integer.parseInt(ParserHelper.takeString(line, "triangles"));
					
					this.triangleIdexes = new ArrayList<Point3i>();
					String[] values;
					int countValidation = 0;
					
					while (it.hasNext()) {
						values = it.next().trim().split(" ");
						
						if (values.length == 3) {
							this.triangleIdexes.add(new Point3i(Integer.parseInt(values[0]), 
									Integer.parseInt(values[1]), Integer.parseInt(values[2])));
							countValidation++;
						}
						
						if (num == countValidation) {
							break;
						} else if (countValidation > num) {
							new FactoryException("error in triangles number");
						}
					}
				
				} else if (line.startsWith("points")) {
					int num = Integer.parseInt(ParserHelper.takeString(line, "points"));
					this.points = new ArrayList<Point3d>();
					String[] values;
					int countValidation = 0;
					while (it.hasNext()) {
						String aux = it.next().trim();
						values = aux.split(" ");
						if (values.length == 3) {
							this.points.add(new Point3d(Double.parseDouble(values[0]), 
									Double.parseDouble(values[1]), Double.parseDouble(values[2])));
							countValidation++;
						}

						if (num == countValidation) {
							break;
						} else if (countValidation > num) {
							new FactoryException("error in triangles number");
						}
					}
				} else if (line.startsWith("transform")) {
					if (line.endsWith(Constants.S_CLOSE)) {
						parseTransform(line);
					} else {
						parseTransform(it);
					}
				} else if (line.startsWith("uvs")) {
					String type = ParserHelper.takeString(line, "uvs");
					if (type.equals("facevarying")) {
						parseUvsFacevarying(it);
					} else if (type.equals("vertex")) {
						parseUvsVertex(it);
					} else {
						//none
						this.uvs = null;
					}
				} else if (line.startsWith("normals")) {
					String type = ParserHelper.takeString(line, "normals");
					if (type.equals("facevarying")) {
						parseNormalsFacevarying(it);
					} else if (type.equals("vertex")) {
						parseNormalsVertex(it);
					} else {
						//none
						this.normals = null;
					}
				}
			}
		} catch (IOException e) {
			throw new FactoryException(e.getMessage());
		} catch (NumberFormatException e) {
			e.printStackTrace();
			throw new FactoryException(e.getMessage());
		}
		
		this.triangles = new ArrayList<TriangleOld>();
	
		if (this.normals != null) {
			for (Point3i index: this.triangleIdexes) {
				Point3d[] vertices = new Point3d[3];
				vertices[0] = this.points.get(index.x);
				vertices[1] = this.points.get(index.y);
				vertices[2] = this.points.get(index.z);
				
				TriangleOld t = new TriangleOld(line, shader, vertices);
				t.normals[0] = new Vector3d(normals.get(vertices[0]));
				t.normals[1] = new Vector3d(normals.get(vertices[1]));
				t.normals[2] = new Vector3d(normals.get(vertices[2]));
				
				this.triangles.add(t);
			}
		}
		GenericMesh ret = new GenericMesh(this.shader, this.name, this.triangles, this.normals, this.uvs);
		
		return transformPresent ? transform(ret) : ret;
	}


	private void parseNormalsVertex(Iterator<String> it) {		
		this.normals = new HashMap<Point3d, Point3d>();
		String[] values;
		int countValidation = 0;
		while (it.hasNext()) {
			values = it.next().trim().split(" ");
			if (values.length == 3) {
				Point3d n = new Point3d(Double.parseDouble(values[0]), 
						Double.parseDouble(values[1]), Double.parseDouble(values[2]));
				this.normals.put(this.points.get(countValidation), n);
				countValidation++;
			}
			
			if (countValidation == this.points.size()) {
				break;
			} else if (countValidation > this.points.size()) {
				new FactoryException("error in triangles normals number");
			}
		}
	}

	private void parseNormalsFacevarying(Iterator<String> it) {
		this.normals = new HashMap<Point3d, Point3d>();
		String[] values;
		int countValidation = 0;
		while (it.hasNext()) {
			values = it.next().trim().split(" ");
			if (values.length == 9) {
				Point3d n1  = new Point3d(Double.parseDouble(values[0]), 
						Double.parseDouble(values[1]), Double.parseDouble(values[2]));
				Point3d n2  = new Point3d(Double.parseDouble(values[3]), 
						Double.parseDouble(values[4]), Double.parseDouble(values[5]));
				Point3d n3  = new Point3d(Double.parseDouble(values[6]), 
						Double.parseDouble(values[7]), Double.parseDouble(values[8]));
				
				this.normals.put(this.points.get(countValidation), n1);
				countValidation++;
				
				this.normals.put(this.points.get(countValidation), n2);
				countValidation++;
				
				this.normals.put(this.points.get(countValidation), n3);
				countValidation++;
			}
			
			if (countValidation == this.points.size()) {
				break;
			} else if (countValidation > this.points.size()) {
				new FactoryException("error in triangles normals facevarying number");
			}
		}
	}

	private void parseUvsVertex(Iterator<String> it) {
		this.uvs = new HashMap<Point3d, Point2d>();
		String[] values;
		int countValidation = 0;
		while (it.hasNext()) {
			values = it.next().trim().split(" ");
			if (values.length == 3) {
				Point2d n = new Point2d(Double.parseDouble(values[0]), 
						Double.parseDouble(values[1]));
				this.uvs.put(this.points.get(countValidation), n);
				countValidation++;
			}
			
			if (countValidation == this.points.size()) {
				break;
			} else if (countValidation > this.points.size()) {
				new FactoryException("error in triangles uvs number");
			}
		}
	}

	private void parseUvsFacevarying(Iterator<String> it) {
		this.uvs = new HashMap<Point3d, Point2d>();
		String[] values;
		int countValidation = 0;
		while (it.hasNext()) {
			values = it.next().trim().split(" ");
			if (values.length == 9) {
				Point2d uv1  = new Point2d(Double.parseDouble(values[0]), 
						Double.parseDouble(values[1]));
				Point2d uv2  = new Point2d(Double.parseDouble(values[2]), 
						Double.parseDouble(values[3]));
				Point2d uv3  = new Point2d(Double.parseDouble(values[4]), 
						Double.parseDouble(values[5]));
				
				this.uvs.put(this.points.get(countValidation), uv1);
				countValidation++;
				
				this.uvs.put(this.points.get(countValidation), uv2);
				countValidation++;
				
				this.uvs.put(this.points.get(countValidation), uv3);
				countValidation++;
			}
			
			if (countValidation == this.points.size()) {
				break;
			} else if (countValidation > this.points.size()) {
				new FactoryException("error in triangles uvs facevarying number");
			}
		}
	}

	public Point3d getTranslate() {
		return translate;
	}
	

}
